#pragma once
#include "Compiler.hpp"
#include "Runner.hpp"
#include "../comm/Log.hpp"
#include "../comm/util.hpp"
#include <jsoncpp/json/json.h>

namespace ns_cr
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_compiler;
    using namespace ns_run;

    class Compiler_Run
    {
    private:

        //删除临时文件
        static void RemoveFile(const std::string file_name)
        {
            std::string _cpp = PathUtil::CPP(file_name);
            if(FileUtil::FILE_IS_EXISTS(_cpp)) unlink(_cpp.c_str());

            std::string _exe = PathUtil::EXE(file_name);
            if(FileUtil::FILE_IS_EXISTS(_exe)) unlink(_exe.c_str());

            std::string _stderror = PathUtil::Stderror(file_name);
            if(FileUtil::FILE_IS_EXISTS(_stderror)) unlink(_stderror.c_str());

            std::string _stdin = PathUtil::Stdin(file_name);
            if(FileUtil::FILE_IS_EXISTS(_stdin)) unlink(_stdin.c_str());

            std::string _stdout = PathUtil::Stdout(file_name);
            if(FileUtil::FILE_IS_EXISTS(_stdout)) unlink(_stdout.c_str());

            std::string _runerr = PathUtil::Run_error(file_name);
            if(FileUtil::FILE_IS_EXISTS(_runerr)) unlink(_runerr.c_str());
        }
        static std::string CodeToDesc(int code, const std::string &file_name)
        {
            std::string desc;
            switch (code)
            {
            case 0:
                desc = "代码运行成功";
                break;
            case -1:
                desc = "代码为空";
                break;
            case -2:
                desc = "发生未知错误"; // 其实是我代码的内部错误，但不能暴露给用户
                break;
            case -3:
                FileUtil::ReadFile(PathUtil::Stderror(file_name), &desc, true); // 编译错误信息打出去
                break;
            case SIGABRT: // 6
                desc = "mem 资源超过范围";
                break;
            case SIGXCPU: // 24
                desc = "CPU 时间到";
                break;
            case SIGFPE: // 8
                desc = "算数错误";
                break;
            default:
                desc = "其他信号或其余未知错误" + std::to_string(code);
                break;
            }
            return desc;
        }

    public:
        Compiler_Run()
        {
        }
        ~Compiler_Run()
        {
        }

        /***************************************************
        *未来要用 http 来接收client发来的str，所以需要保存用户的:
        * 输入(用户给服务器的):
        *1.code : 用户提交的代码字段
        *2.input: 用户的输入（不考虑）
        *未来处理 输入模块 的东西时，还得限制 时间 和 空间 ——>cpu mem limit
        *
        * 输出(服务器给用户的):
        *1.status: 状态码（实现：退出码 / 信号）
        *2.reason: 请求的结果
        *3.stdout: 代码运行完的结果
        *4.stderr: 代码运行完的错误信息
        *
        * 参数:
        * in_json: {"code": "#include...", "input": "","cpu_limit":1, "mem_limit":10240}
        * out_json: {"status":"0", "reason":"","stdout":"","stderr":"",}
        *
        *
        **************************************************/

        // 用户传进来的是序列化好的串，需要反序列化读取
        // 我们传出去的得经过序列化发出去
        static void Start(const std::string in_jason, std::string *out_jason)
        {
            Json::Value in_value;
            Json::Reader reader;
            // 将in_jason转化为结构化数据，存储在value中
            reader.parse(in_jason, in_value);
            std::string code = in_value["code"].asString();
            std::string input = in_value["input"].asString();
            // int cpu_time = in_value["cpu_time"].asInt();
            // int mem_space = in_value["mem_space"].asInt();

            int cpu_rlimit = in_value["cpu_rlimit"].asInt();
            int mem_rlimit = in_value["mem_rlimit"].asInt();

            // 把输出的东西回显给用户
            Json::Value out_value;
            int status_code = 0;
            std::string file_name;
            int run_code = 0;
            if (code.size() == 0)
            {
                status_code = -1;
                goto END;
            }

            // 给用户提交的不同代码，都整一个唯一的文件名
            // 方法:毫秒级时间戳+原子性递增唯一值: 来保证唯一性
            file_name = FileUtil::CreatFileName(); // TODO

            // 把用户提交的代码，写到创建的.cpp文件里
            if (!FileUtil::WriteFile(PathUtil::CPP(file_name), code))
            {
                status_code = -2;
                goto END;
            }

            if (!Compiler::Compile(file_name))
            {
                status_code = -3;
                goto END;
            }

            run_code = Runner::Run(file_name, cpu_rlimit, mem_rlimit);
            if (run_code < 0)
            {
                // 内部错误
                status_code = -2;
            }
            else if (run_code > 0)
            {
                // 程序运行崩溃（信号中断）
                status_code = run_code;
            }

            else
            {
                // 运行成功
                status_code = 0;
            }

        // 现在已经运行完了，接下来把输出的东西回显给用户
        END:
            out_value["status"] = status_code;
            out_value["reason"] = CodeToDesc(status_code, file_name);

            //std::cout << "看看有没有转换 " << out_value["reason"] <<std::endl;
            if (status_code == 0)
            {
                std::string stdout;
                FileUtil::ReadFile(PathUtil::Stdout(file_name),&stdout,true);
                out_value["stdout"] = stdout;
                
                std::string stderr;
                FileUtil::ReadFile(PathUtil::Stderror(file_name),&stderr,true);
                out_value["stderror"] = stderr;
            }

            //Json::StyledWriter writer;
	    Json::StreamWriterBuilder writer;
	    writer["emitUTF8"] = true;
            //*out_jason = writer.write(out_value);
	    *out_jason = Json::writeString(writer,out_value);

            RemoveFile(file_name);
        }
    };
}

// out_value["statue"] = 1;
// out_value["reason"] = "代码为空";

// out_value["status"] = 2;
// out_value["reason"] = "发生未知错误"; // 其实是我服务端没有把代码成功写进文件，但不能暴露给用户

// out_value["statue"] = 3;
// out_value["reason"] = FileUtil::ReadFile(PathUtil::Stderror(file_name)); // 编译错误信息打出去

// out_value["status"] = 2;
// out_value["reason"] = "发生未知错误"; // 其实是我内部有问题

// out_value["status"] = 4;
// out_value["reason"] = SigToDesc(n);
